﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Roton.Memory
{
    class Segment
    {
        private byte[] mem;

        /// <summary>
        /// Create a memory segment of the specified size.
        /// </summary>
        /// <param name="memorySize">Size in bytes of the memory segment to allocate.</param>
        public Segment(int memorySize)
        {
            mem = new byte[memorySize];
        }

        /// <summary>
        /// Dump memory.
        /// </summary>
        public byte[] Dump()
        {
            byte[] result = new byte[mem.Length];
            Array.Copy(mem, result, result.Length);
            return result;
        }

        /// <summary>
        /// Read bytes from the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to read from.</param>
        /// <param name="length">Number of bytes to read.</param>
        /// <returns>Data read from the segment.</returns>
        public byte[] Read(int offset, int length)
        {
            if (offset <= 0)
                return new byte[] { };
            byte[] result = new byte[length];
            Array.Copy(mem, offset, result, 0, length);
            return result;
        }

        /// <summary>
        /// Read an 8-bit value from the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to read from.</param>
        /// <returns>Data read from the segment.</returns>
        public int Read8(int offset)
        {
            if (offset <= 0)
                return 0;
            return mem[offset];
        }

        /// <summary>
        /// Read a 16-bit value from the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to read from.</param>
        /// <returns>Data read from the segment.</returns>
        public int Read16(int offset)
        {
            if (offset <= 0)
                return 0;
            Int32 result = mem[offset + 1];
            result <<= 8;
            result |= mem[offset];
            // make this a signed value
            result <<= 16;
            result >>= 16;
            return result;
        }

        /// <summary>
        /// Read a 32-bit value from the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to read from.</param>
        /// <returns>Data read from the segment.</returns>
        public int Read32(int offset)
        {
            if (offset <= 0)
                return 0;
            Int32 result = mem[offset + 3];
            result <<= 8;
            result |= mem[offset + 2];
            result <<= 8;
            result |= mem[offset + 1];
            result <<= 8;
            result |= mem[offset];
            return result;
        }

        /// <summary>
        /// Read a boolean value from the memory segment. 0 is false, all other values are true.
        /// </summary>
        /// <param name="offset">Offset in bytes to read from.</param>
        /// <returns>Data read from the segment.</returns>
        public bool ReadBoolean(int offset)
        {
            if (offset <= 0)
                return false;
            return (mem[offset] != 0);
        }

        /// <summary>
        /// Read a string value from the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to read from.</param>
        /// <returns>Data read from the segment.</returns>
        public ByteString ReadString(int offset)
        {
            if (offset <= 0)
                return new ByteString();
            int length = mem[offset];
            byte[] result = new byte[length];
            Array.Copy(mem, offset + 1, result, 0, length);
            return new ByteString(result);
        }

        /// <summary>
        /// Write bytes to the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to write to.</param>
        /// <param name="value">Data to write to the segment.</param>
        public void Write(int offset, byte[] value)
        {
            if (offset < 0)
                return;
            Array.Copy(value, 0, mem, offset, value.Length);
        }

        /// <summary>
        /// Write an 8-bit value to the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to write to.</param>
        /// <param name="value">Data to write to the segment.</param>
        public void Write8(int offset, int value)
        {
            if (offset <= 0)
                return;
            mem[offset] = (byte)(value & 0xFF);
        }

        /// <summary>
        /// Write a 16-bit value to the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to write to.</param>
        /// <param name="value">Data to write to the segment.</param>
        public void Write16(int offset, int value)
        {
            if (offset <= 0)
                return;
            mem[offset] = (byte)(value & 0xFF);
            mem[offset + 1] = (byte)((value >> 8) & 0xFF);
        }

        /// <summary>
        /// Write a 32-bit value to the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to write to.</param>
        /// <param name="value">Data to write to the segment.</param>
        public void Write32(int offset, int value)
        {
            if (offset <= 0)
                return;
            mem[offset] = (byte)(value & 0xFF);
            mem[offset + 1] = (byte)((value >> 8) & 0xFF);
            mem[offset + 2] = (byte)((value >> 16) & 0xFF);
            mem[offset + 3] = (byte)((value >> 24) & 0xFF);
        }

        /// <summary>
        /// Write a boolean value to the memory segment. False is written as 0, true is written as 1.
        /// </summary>
        /// <param name="offset">Offset in bytes to write to.</param>
        /// <param name="value">Data to write to the segment.</param>
        public void WriteBoolean(int offset, bool value)
        {
            if (offset <= 0)
                return;
            mem[offset] = value ? (byte)0x01 : (byte)0x00;
        }

        /// <summary>
        /// Write a string value to the memory segment.
        /// </summary>
        /// <param name="offset">Offset in bytes to write to.</param>
        /// <param name="value">Data to write to the segment.</param>
        public void WriteString(int offset, ByteString value)
        {
            if (offset <= 0)
                return;
            int length = value.Length & 0xFF;
            mem[offset] = (byte)length;
            Array.Copy(value, 0, mem, offset + 1, length);
        }
    }
}
